#include "codeview.hpp"
#include "ui_codeview.h"

CodeView::CodeView(QWidget *parent) :
    QDockWidget(parent),
    ui(new Ui::CodeView)
{
    ui->setupUi(this);

    this->highlightFormat = new QTextBlockFormat();
    this->highlightFormat->setBackground(QBrush(QColor(0, 180, 0)));
    this->currentLineFormat = new QTextBlockFormat();
    this->currentLineFormat->setBackground(QBrush(QColor(220, 220, 220)));
    this->simulationStoppedOnLine = -1;

    // Handle Events on the LineNumbers to trigger Breakpoints
    this->textEditEventHandler = new TextEditEventHandler();
    connect(this->textEditEventHandler, SIGNAL(requestBreakPoint(QPoint)), this, SLOT(onRequestBreakpoint(QPoint)));
    connect(this->textEditEventHandler, SIGNAL(requestHighlightLine(QPoint,QPoint)), this, SLOT(onRequestHighlightLine(QPoint,QPoint)));
    ui->txt_Additional->viewport()->installEventFilter(this->textEditEventHandler);

    // Handle Tooltip Events
    this->tooltipEventHandler = new ToolTipEventHandler();
    connect(this->tooltipEventHandler, SIGNAL(tooltipRequested(QPoint*)), this, SLOT(onTooltipRequested(QPoint*)));
    ui->txt_Code->viewport()->installEventFilter(this->tooltipEventHandler);
    connect(this->tooltipEventHandler, SIGNAL(zoomEvent(int)), ui->txt_Additional, SLOT(zoomIn(int)));
    connect(this->tooltipEventHandler, SIGNAL(zoomEvent(int)), ui->txt_Code, SLOT(zoomIn(int)));

    // Make up the Syntax Highlighting
    this->highlighter = new Highlighter(ui->txt_Code->document());
    connect(this->highlighter, SIGNAL(customTooltip(QString,QString)), this, SLOT(onCustomTooltip(QString,QString)));
    this->highlighter->initialize();

    // Synchronise both Scrollbars
    connect(ui->txt_Code->verticalScrollBar(), SIGNAL(valueChanged(int)),
            ui->txt_Additional->verticalScrollBar(), SLOT(setValue(int)));
    connect(ui->txt_Additional->verticalScrollBar(), SIGNAL(valueChanged(int)),
	    ui->txt_Code->verticalScrollBar(), SLOT(setValue(int)));
    ui->txt_Code->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    ui->txt_Additional->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}

CodeView::~CodeView()
{
    delete this->highlighter;
    delete this->highlightFormat;
    delete this->currentLineFormat;
    delete this->textEditEventHandler;
    delete this->tooltipEventHandler;

    delete ui;
}

void CodeView::changeEvent(QEvent *e)
{
    QDockWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void CodeView::addCommands(const QList<Command> *commandList)
{
    for(int i = 0; i < commandList->size(); i++)
    {
	addCommandLine(commandList->at(i));
    }
    ui->txt_Code->verticalScrollBar()->setValue(0);
}

void CodeView::addCommandLine(const Command &command)
{
    QString lnNumber = QString::number(command.getLineNumber());

    while (lnNumber.size() < 5)
	lnNumber.prepend("0");

    ui->txt_Additional->append(lnNumber);

    /* TODO: Add Formats to the Command */
    QString text;
    QString cmd;
    switch (command.getCommandType())
    {
    case Command::COMMAND:
	cmd = command.toString();
	cmd.replace(" ", "&nbsp;", Qt::CaseInsensitive);
	text.append("<a id=\"").append(command.getAnchor()).append("\" name=\"").append(command.getAnchor()).append("\">");
	text.append("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;").append(cmd).append("</a>");
	this->anchorMap.insert(command.getAnchor(), command.getLineNumber());
	ui->txt_Code->append(text);
	break;

    case Command::LABEL:
    case Command::DEFINITION:
	cmd = command.toString();
	cmd.replace(" ", "&nbsp;", Qt::CaseInsensitive);
	text.append("<a name=\"").append(command.getAnchor()).append("\">");
	text.append(cmd).append("</a>");
	this->anchorMap.insert(command.getAnchor(), command.getLineNumber());
	ui->txt_Code->append(text);
	break;

    case Command::ADDITIONAL:
    case Command::COMMENT:
	ui->txt_Code->append(command.toString());
	break;

    case Command::BLANK:
    default:
	ui->txt_Code->append(command.toString());
	break;
    }
}


void CodeView::reset()
{
    ui->txt_Additional->clear();
    ui->txt_Code->clear();

    this->anchorMap.clear();
    this->highlightedLines = false;
    this->highlightedPc = false;
    this->simulationStoppedOnLine = -1;
}


void CodeView::onRequestBreakpoint(QPoint point)
{
    QTextCursor text = ui->txt_Additional->cursorForPosition(point);
    QString line = text.block().text();
    line.truncate(5);

    emit requestBreakpoint(line.toInt(0, 10));
}


void CodeView::onRequestHighlightLine(QPoint point, QPoint startPoint)
{
    if (ui->txt_Code->document()->lineCount() == 1)
	return;

    QTextCursor cursor1 = ui->txt_Additional->cursorForPosition(point);
    cursor1.select(QTextCursor::WordUnderCursor);
    QTextCursor cursor2 = ui->txt_Additional->cursorForPosition(startPoint);
    cursor2.select(QTextCursor::WordUnderCursor);

    this->onRequestUnHighlightLine();
    this->highlightLines(cursor1.selectedText().toInt(0, 10), cursor2.selectedText().toInt(0, 10));
}


void CodeView::highlightLines(int p1, int p2)
{
    int start, length;

    if (p1 > p2)
    {
	start = p2;
	length = p1 - p2;
    } else {
	start = p1;
	length = p2 - p1;
    }

    QTextCursor codeCursor = ui->txt_Code->textCursor();
    QTextCursor lineCursor = ui->txt_Additional->textCursor();

    codeCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
    lineCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
    codeCursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, start - 1);
    lineCursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, start - 1);

    codeCursor.beginEditBlock();
    lineCursor.beginEditBlock();

    do {
	codeCursor.setBlockFormat(*this->highlightFormat);
	lineCursor.setBlockFormat(*this->highlightFormat);
	codeCursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
	lineCursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
    } while (length-- > 0);

    codeCursor.endEditBlock();
    lineCursor.endEditBlock();

    this->highlightedLines = true;
}


void CodeView::onRequestUnHighlightLine()
{
    if (this->highlightedLines)
    {
	ui->txt_Code->undo();
	ui->txt_Additional->undo();
	this->highlightedLines = false;
    }

    if (this->highlightedPc)
    {
	ui->txt_Code->undo();
	this->highlightedPc = false;
    }

    if (this->simulationStoppedOnLine > -1)
    {
	QTextCursor cursor = ui->txt_Additional->textCursor();
	cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
	cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, this->simulationStoppedOnLine - 1);
	cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
	cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
	cursor.removeSelectedText();
	ui->txt_Additional->setTextCursor(cursor);
	this->simulationStoppedOnLine = -1;
    }
}


void CodeView::onCustomHighlightingRule(QString pattern, int type)
{
    this->highlighter->addCustomHighlightingRule(pattern.trimmed(), type);
}


void CodeView::onBreakpointToggled(int line, bool add)
{
    this->onRequestUnHighlightLine();;

    if (add)
    {
	//Add Breakpoint
	QTextCursor cursor = ui->txt_Additional->textCursor();
	cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
	cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, line - 1);
	cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
	if (this->simulationStoppedOnLine == line)
	    cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor);
	cursor.insertImage(":/images/breakpoint");

	//Set Highlight
	this->highlightLines(line, line);

	ui->txt_Additional->setTextCursor(cursor);
	ui->txt_Additional->ensureCursorVisible();
    } else {
	//Remove Breakpoint
	QTextCursor cursor = ui->txt_Additional->textCursor();
	cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
	cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, line - 1);
	cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
	if (this->simulationStoppedOnLine == line)
	    cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor);
	cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
	cursor.removeSelectedText();
	ui->txt_Additional->setTextCursor(cursor);
	ui->txt_Additional->ensureCursorVisible();
    }
}


void CodeView::onProgramCounterChanged(int pc)
{
    this->onRequestUnHighlightLine();

    QString anchor = QString::number(pc, 16);
    while (anchor.size() < 4)
	anchor.prepend("0");
    ui->txt_Code->scrollToAnchor(anchor.toUpper());

    QTextCursor codeCursor = ui->txt_Code->textCursor();
    codeCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
    codeCursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, this->anchorMap.find(anchor.toUpper()).value() - 1);
    codeCursor.setBlockFormat(*this->currentLineFormat);

    this->highlightedPc = true;
}


void CodeView::onTooltipRequested(QPoint *point)
{
    QTextCursor cursor = ui->txt_Code->cursorForPosition(*point);
    cursor.select(QTextCursor::WordUnderCursor);

    QString item = cursor.selectedText().trimmed().toLower();
    if (this->tooltipMap.contains(item))
	QToolTip::showText(ui->txt_Code->mapToGlobal(*point), this->tooltipMap.find(item).value());
}


void CodeView::onCustomTooltip(QString item, QString tooltip)
{
    item = item.trimmed().toLower();

    if (this->tooltipMap.contains(item))
	this->tooltipMap.remove(item);

    this->tooltipMap.insert(item, tooltip);
}


void CodeView::onSimulationStateChanged(bool state, int pc)
{
    this->onRequestUnHighlightLine();

    QString anchor = QString::number(pc, 16);
    while (anchor.size() < 4)
	anchor.prepend("0");
    int line = this->anchorMap.find(anchor.toUpper()).value();

    if (!state) {
	this->simulationStoppedOnLine = line;
	QTextCursor cursor = ui->txt_Additional->textCursor();
	cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
	cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, line - 1);
	cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
	cursor.insertImage(":/images/current");
	ui->txt_Additional->setTextCursor(cursor);
    }
}


void CodeView::onJumpToBreakpoint(QString anchor)
{
    ui->txt_Code->scrollToAnchor(anchor);
}
